package com.logic.modular.game.signIn.service.impl;

import com.comment.exception.ServiceException;
import com.logic.modular.game.statistical.domain.PlayerStatisticalPool;
import com.logic.modular.game.statistical.service.impl.PlayerStatisticalPoolServiceImpl;
import com.logic.modular.game.signIn.service.PlayerSignInService;
import org.jetbrains.annotations.NotNull;

import org.redisson.api.RBitSet;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.*;
import java.util.*;

/**
 * 用户签到服务实现类
 */
@Service
public class PlayerSignInServiceImpl implements PlayerSignInService {
    String USER_SIGN_IN_REDIS_KEY_PREFIX = "player:signing";

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private PlayerStatisticalPoolServiceImpl playerStatisticalPoolService;

    private @NotNull String getKey(long userId, Integer year) {
        return String.format("%s:%d:%d", USER_SIGN_IN_REDIS_KEY_PREFIX, year, userId);
    }

    private BitSet getBitSet(long userId, Integer year) {
        if (year == null) {
            LocalDate date = LocalDate.now();
            year = date.getYear();
        }
        // 根据当前年和用户id获取key
        String key = getKey(userId, year);
        // 获取redis的BitMap
        RBitSet signInBitSet = redissonClient.getBitSet(key);
        // 注意   加载bitSet到内存中，避免后续读取时发送多次请求
        return signInBitSet.asBitSet();
    }

    @Override
    public boolean signIn(long userId) {
        // 获取当前时间
        LocalDate date = LocalDate.now();
        // 获取当前年份
        int year = date.getYear();
        // 拼接redis key
        String key = getKey(userId, year);
        // 获取redis的操作对象
        RBitSet bitSet = redissonClient.getBitSet(key);
        // 获取当前日期是一年中的第几天，作为偏移量
        int offSet = date.getDayOfYear();
        // 判断redis中key对应的天数是否已经签到 true就是已签到
        if (!bitSet.get(offSet)) {
            // 能查到就是true因为加了！取反 就签到
            bitSet.set(offSet, true); // true就是已签到

            PlayerStatisticalPool obtain = playerStatisticalPoolService.obtain(userId);
            long l = obtain.conditionValue2.get(60) + 1;
            obtain.conditionValue2.add(60, l);
            playerStatisticalPoolService.updateById(obtain);
        }
        return true;
    }

    @Override
    public List<Integer> getUserSignInOfYear(long userId, Integer year) {
        BitSet bitSet = getBitSet(userId, year);
        // 这里使用LinKefHashMap是因为放入和取出的顺序有序
        List<Integer> list = new ArrayList<>();
        // 从索引0开始查找下一个设置为1的位置的索引 查找不到返回-1即不等于-1就一直循环
        int index = bitSet.nextSetBit(0);
        while (index != -1) {
            list.add(index);
            // 找到下一个设置为1的位置 索引位置要+1
            index = bitSet.nextSetBit(index + 1);
        }
        return list;
    }


    @Override
    public List<Integer> getUserSignInRecord(long userId, Integer year, Integer startDay, Integer endDay) {
        BitSet bitSet = getBitSet(userId, year);

        List<Integer> list = new ArrayList<>();
        int index = bitSet.nextSetBit(startDay);
        while (index != -1 && index <= endDay) {
            list.add(index);
            // 找到下一个设置为1的位置 索引位置要+1
            index = bitSet.nextSetBit(index + 1);
        }
        return list;
    }

    @Override
    public boolean supplementarySignIn(Long playerId, Integer dayOfMonth) {
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        if (dayOfMonth >= now.getDayOfMonth()) {
            throw new ServiceException("补签天数不合规");
        }
        // 根据当前年和用户id获取key
        String key = getKey(playerId, year);
        // 获取redis的BitMap
        RBitSet bitSet = redissonClient.getBitSet(key);
        // 获取 补 签 的天数
        LocalDate targetDay = now.withDayOfMonth(dayOfMonth);
        // 获取年日
        int offSet = targetDay.getDayOfYear();
        // 签到
        if (!bitSet.get(offSet)) {
            // 能查到就是true因为加了！取反 就签到
            bitSet.set(offSet, true); // true就是已签到

            PlayerStatisticalPool obtain = playerStatisticalPoolService.obtain(playerId);
            long l = obtain.conditionValue2.get(60) + 1;
            obtain.conditionValue2.add(60, l);
            playerStatisticalPoolService.updateById(obtain);
        }
        return true;
    }
}
